package com.hyt.it.ogt.kq.service.gov.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import com.google.common.collect.Maps;
import com.hyt.it.ogt.kq.common.gov.utils.TextUtil;
import com.hyt.it.ogt.kq.service.gov.mapper.TaskRoomMapper;
import com.hyt.it.ogt.kq.service.gov.mapper.TimeMapper;
import com.hyt.it.ogt.kq.service.gov.model.entity.*;
import com.hyt.it.ogt.kq.service.gov.model.param.arrange.ArrangeResultByTimeParam;
import com.hyt.it.ogt.kq.service.gov.model.vo.arrange.ArrangeResultByTimeVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hyt.core.base.BaseServiceImpl;
import com.hyt.core.util.UUIDUtils;
import com.hyt.it.ogt.kq.common.config.exception.KqException;
import com.hyt.it.ogt.kq.common.gov.constant.ResponseCode;
import com.hyt.it.ogt.kq.common.gov.constant.TaskExamTypeEnum;
import com.hyt.it.ogt.kq.common.gov.utils.timeslot.Timeslot;
import com.hyt.it.ogt.kq.common.gov.utils.timeslot.TimeslotUtil;
import com.hyt.it.ogt.kq.service.gov.mapper.TimeRoomMapper;
import com.hyt.it.ogt.kq.service.gov.model.dto.RoomDataPackageDTO;
import com.hyt.it.ogt.kq.service.gov.model.dto.TimeRoomInfoDTO;
import com.hyt.it.ogt.kq.service.gov.model.dto.TimeRoomInfoResultDTO;
import com.hyt.it.ogt.kq.service.gov.model.dto.arrange.CandidateSubjectDTO;
import com.hyt.it.ogt.kq.service.gov.model.dto.examiner.RoomExaminerDTO;
import com.hyt.it.ogt.kq.service.gov.model.param.PageRoomPackageParam;
import com.hyt.it.ogt.kq.service.gov.model.vo.RoomPackageVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.TaskPlaceVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.TaskRoomToTimeParamVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.TaskTimeRoomArrangeOrNoVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.TimeRoomCodeVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.TimeRoomVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.roomdata.UnArrangeRoomTimeInfoVO;
import com.hyt.it.ogt.kq.service.gov.service.ITaskRoomService;
import com.hyt.it.ogt.kq.service.gov.service.ITaskService;
import com.hyt.it.ogt.kq.service.gov.service.ITimeRoomParamService;
import com.hyt.it.ogt.kq.service.gov.service.ITimeRoomService;
import com.hyt.it.ogt.kq.service.gov.service.ITimeService;

import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * 批次考场表 服务实现类
 * </p>
 *
 * @author huangyh
 * @since 2021-03-08
 */
@Slf4j
@Service
public class TimeRoomServiceImpl extends BaseServiceImpl<TimeRoomMapper, TimeRoom> implements ITimeRoomService {

    @Autowired
    private ITaskRoomService iTaskRoomService;

    @Resource
    private ITaskService iTaskService;

    @Resource
    private ITimeRoomParamService iTimeRoomParamService;

    @Resource
    private ITimeService iTimeService;

    @Resource
    private TimeMapper timeMapper;

    @Resource
    private TaskRoomMapper taskRoomMapper;


    public Map<String,String> buildTimeRoomMap(String taskId){
        Map<String, String> timeRoomIdMap = new HashMap<>();
        List<Time> timeList = iTimeService.getByTaskId(taskId);
        List<TaskRoom> roomList = iTaskRoomService.lambdaQuery().eq(TaskRoom::getTaskId, taskId).eq(TaskRoom::getDelFlag, false).list();
        for(Time time : timeList){
            for (TaskRoom taskRoom : roomList) {
                String timeRoomId = UUIDUtils.newSortUUID();
                timeRoomIdMap.put(time.getId()+"_"+taskRoom.getId(), timeRoomId);
            }
        }
        return timeRoomIdMap;
    }

    /**
     * 批次绑定考场
     * @param taskRoomToTimeParamVO
     */
    @Override
    public void createTimeRoom(TaskRoomToTimeParamVO taskRoomToTimeParamVO) {


        String taskId = taskRoomToTimeParamVO.getTaskId();
        String timeId = taskRoomToTimeParamVO.getTimeId();

        List<TaskTimeRoomArrangeOrNoVO> taskTimeRoomArrangeOrNoVOList = taskRoomToTimeParamVO.getTaskTimeRoomArrangeOrNoVOList();
        //如果是空，则全部取消
        if (CollectionUtils.isEmpty(taskTimeRoomArrangeOrNoVOList)) {
            LambdaUpdateWrapper<TimeRoom> timeRoomLambdaUpdateWrapper = Wrappers.lambdaUpdate();
            timeRoomLambdaUpdateWrapper.eq(TimeRoom::getTaskId,taskId)
                    .eq(TimeRoom::getTimeId,timeId)
                    .eq(TimeRoom::getDelFlag,false)
                    .eq(TimeRoom::getSelectType,"0")
                    .set(TimeRoom::getDelFlag,true);
            this.update(timeRoomLambdaUpdateWrapper);
            return;
        }

        //取出roomId
        List<String> roomIds = taskRoomToTimeParamVO.getTaskTimeRoomArrangeOrNoVOList()
                .stream().map(TaskTimeRoomArrangeOrNoVO::getRoomId).collect(Collectors.toList());
        List<TaskTimeRoomArrangeOrNoVO> vos = taskRoomMapper.selectTimeRoomByTaskIdAndTimeId(taskId,timeId,roomIds);

        //查询已考场数据
        LambdaQueryWrapper<TimeRoom> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(TimeRoom::getTaskId,taskId)
                .eq(TimeRoom::getTimeId,timeId)
                .eq(TimeRoom::getSelectType,"0")
                .eq(TimeRoom::getDelFlag,false);
        List<TimeRoom> timeRoomList = baseMapper.selectList(queryWrapper);
        StringBuilder roomBuilder = new StringBuilder();
        timeRoomList.stream().forEach(timeRoom -> {
            roomBuilder.append(timeRoom.getRoomId()).append(",");
        });

        //批次绑定考场时，校验相同考场中是否有批次时间冲突的批次
        this.hasTimeOverlap(taskRoomToTimeParamVO);

        //新增绑定考场
        List<TimeRoom> addTimeRoomList = new ArrayList<>();
        StringBuilder delRoomBuilder = new StringBuilder();
        int code = this.getLastTimeRoomCode(taskId);
        for (TaskTimeRoomArrangeOrNoVO taskTimeRoomArrangeOrNoVO : taskTimeRoomArrangeOrNoVOList) {
            if (!roomBuilder.toString().contains(taskTimeRoomArrangeOrNoVO.getRoomId())) {
                TimeRoom timeRoom = new TimeRoom();
                code = code + 1;
                timeRoom.setTaskId(taskId)
                        .setTimeId(timeId)
                        .setRoomId(taskTimeRoomArrangeOrNoVO.getRoomId())
                        .setMaxCount(Integer.parseInt(taskTimeRoomArrangeOrNoVO.getSeatNum()))
                        .setPlanCount(Integer.parseInt(taskTimeRoomArrangeOrNoVO.getPlanCount()))
                        .setActualCount(0)
                        //.setSelectType(taskRoomToTimeParamVO.getSelectType())
                        .setCode(String.valueOf(code))
                        .setId(UUIDUtils.newSortUUID());
                addTimeRoomList.add(timeRoom);
            }
            delRoomBuilder.append(taskTimeRoomArrangeOrNoVO.getRoomId()).append(",");
        }
        if (!CollectionUtils.isEmpty(addTimeRoomList)) {
            this.saveBatch(addTimeRoomList);
        }

        //删除绑定的考场
        List<String> delTimeRoomList = new ArrayList<>();
        timeRoomList.stream().forEach(timeRoom -> {
            //如果已经编排了，则不允许删除
            if (!delRoomBuilder.toString().contains(timeRoom.getRoomId()) && timeRoom.getActualCount() == 0) {
                delTimeRoomList.add(timeRoom.getId());
            }
        });

        if (!CollectionUtils.isEmpty(delTimeRoomList)) {
            LambdaUpdateWrapper<TimeRoom> timeRoomLambdaUpdateWrapper = Wrappers.lambdaUpdate();
            timeRoomLambdaUpdateWrapper.in(TimeRoom::getId,delTimeRoomList)
                    .set(TimeRoom::getDelFlag,true);
            this.update(timeRoomLambdaUpdateWrapper);
        }

    }

    /**
     * 批次排除考场
     * @param taskRoomToTimeParamVO
     */
    @Override
    public void unBindTimeRoom(TaskRoomToTimeParamVO taskRoomToTimeParamVO){
        String taskId = taskRoomToTimeParamVO.getTaskId();
        String timeId = taskRoomToTimeParamVO.getTimeId();
        List<TaskTimeRoomArrangeOrNoVO> taskTimeRoomArrangeOrNoVOList = taskRoomToTimeParamVO.getTaskTimeRoomArrangeOrNoVOList();
        //如果是空，则全部取消
        if (CollectionUtils.isEmpty(taskTimeRoomArrangeOrNoVOList)) {
            LambdaUpdateWrapper<TimeRoom> timeRoomLambdaUpdateWrapper = Wrappers.lambdaUpdate();
            timeRoomLambdaUpdateWrapper.eq(TimeRoom::getTaskId,taskId)
                    .eq(TimeRoom::getTimeId,timeId)
                    .eq(TimeRoom::getDelFlag,false)
                    .eq(TimeRoom::getSelectType,"1")
                    .set(TimeRoom::getDelFlag,true);
            this.update(timeRoomLambdaUpdateWrapper);
            return;
        }

        //查询已考场数据
        LambdaQueryWrapper<TimeRoom> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(TimeRoom::getTaskId,taskId)
                .eq(TimeRoom::getTimeId,timeId)
                .eq(TimeRoom::getSelectType,"1")
                .eq(TimeRoom::getDelFlag,false);
        List<TimeRoom> timeRoomList = baseMapper.selectList(queryWrapper);
        StringBuilder roomBuilder = new StringBuilder();
        timeRoomList.stream().forEach(timeRoom -> {
            roomBuilder.append(timeRoom.getRoomId()).append(",");
        });

        //新增排除考场
        List<TimeRoom> unTimeRoomList = new ArrayList<>();
        StringBuilder delRoomBuilder = new StringBuilder();
        int code = this.getLastTimeRoomCode(taskId);
        for (TaskTimeRoomArrangeOrNoVO taskTimeRoomArrangeOrNoVO : taskRoomToTimeParamVO.getTaskTimeRoomArrangeOrNoVOList()) {
            if (!roomBuilder.toString().contains(taskTimeRoomArrangeOrNoVO.getRoomId())) {
                TimeRoom timeRoom = new TimeRoom();
                code = code + 1;
                timeRoom.setTaskId(taskId)
                        .setTimeId(timeId)
                        .setRoomId(taskTimeRoomArrangeOrNoVO.getRoomId())
                        .setMaxCount(Integer.parseInt(taskTimeRoomArrangeOrNoVO.getSeatNum()))
                        .setPlanCount(Integer.parseInt(taskTimeRoomArrangeOrNoVO.getPlanCount()))
                        .setActualCount(0)
                        //.setSelectType(taskRoomToTimeParamVO.getSelectType())
                        .setCode(String.valueOf(code))
                        .setId(UUIDUtils.newSortUUID());
                unTimeRoomList.add(timeRoom);
            }
            delRoomBuilder.append(taskTimeRoomArrangeOrNoVO.getRoomId()).append(",");
        }
        if (!CollectionUtils.isEmpty(unTimeRoomList)) {
            this.saveBatch(unTimeRoomList);
        }

        //删除排除的考场
        List<String> delTimeRoomList = new ArrayList<>();
        timeRoomList.stream().forEach(timeRoom -> {
            //如果已经编排了，则不允许删除
            if (!delRoomBuilder.toString().contains(timeRoom.getRoomId()) && timeRoom.getActualCount() == 0) {
                delTimeRoomList.add(timeRoom.getId());
            }
        });

        if (!CollectionUtils.isEmpty(delTimeRoomList)) {
            LambdaUpdateWrapper<TimeRoom> timeRoomLambdaUpdateWrapper = Wrappers.lambdaUpdate();
            timeRoomLambdaUpdateWrapper.in(TimeRoom::getId,delTimeRoomList)
                    .set(TimeRoom::getDelFlag,true);
            this.update(timeRoomLambdaUpdateWrapper);
        }
    }

    /**
     * 批次绑定考场时，校验相同考场中是否有批次时间冲突的批次
     * @param taskRoomToTimeParamVO 批次绑定考场参数
     */
    private void hasTimeOverlap(TaskRoomToTimeParamVO taskRoomToTimeParamVO) {
        //1、获取当前批次绑定的场次信息（包括“已绑定的”和“将要绑定的”）
        //获取当前批次已绑定的场次信息
        List<TimeRoomInfoDTO> timeRoomInfoDTOList = baseMapper.listTimeRoomInfoDTO(taskRoomToTimeParamVO.getTaskId(),
                taskRoomToTimeParamVO.getTimeId(), null);
        //获取当前批次将要绑定的场次信息
        List<String> roomIdList = taskRoomToTimeParamVO.getTaskTimeRoomArrangeOrNoVOList().stream()
                .map(TaskTimeRoomArrangeOrNoVO::getRoomId)
                .collect(Collectors.toList());
        List<TaskRoom> taskRoomList = iTaskRoomService.lambdaQuery()
                .in(TaskRoom::getPtRoomId, roomIdList)
                .eq(TaskRoom::getTaskId, taskRoomToTimeParamVO.getTaskId())
                .eq(TaskRoom::getDelFlag, Boolean.FALSE)
                .list();
        Time time = iTimeService.lambdaQuery()
                .eq(Time::getId, taskRoomToTimeParamVO.getTimeId())
                .eq(Time::getTaskId, taskRoomToTimeParamVO.getTaskId())
                .eq(Time::getDelFlag, Boolean.FALSE)
                .last("LIMIT 1")
                .one();
        //合并当前批次绑定的场次信息
        taskRoomList.forEach(room -> {
            TimeRoomInfoDTO timeRoomInfoDTO = new TimeRoomInfoDTO();
            timeRoomInfoDTO.setRoomId(room.getId())
                    .setRoomName(room.getName())
                    .setTimeId(time.getId())
                    .setTimeName(time.getName())
                    .setStartTime(time.getStartTime())
                    .setEndTime(time.getEndTime());
            timeRoomInfoDTOList.add(timeRoomInfoDTO);
        });
        //2、获取当前批次绑定的考场所对应的其他批次的场次信息（即：[当前批次]绑定[考场1]，[批次2]绑定[考场1]，获取[批次2]绑定[考场1]的场次信息）
        //获取当前批次绑定的考场id
        String roomIds = timeRoomInfoDTOList.stream()
                .map(TimeRoomInfoDTO::getRoomId)
                .distinct()
                .collect(Collectors.joining(","));
        //根据当前批次绑定的考场id，获取当前批次绑定的考场所对应的其他批次的场次信息
        List<TimeRoomInfoDTO> othersTimeRoomInfoDTOList = baseMapper
                .listTimeRoomInfoDTO(taskRoomToTimeParamVO.getTaskId(), null, roomIds);
        //过滤掉当前批次的场次信息
        List<TimeRoomInfoDTO> othersList = othersTimeRoomInfoDTOList.stream()
                .filter(other -> !other.getTimeId().equals(taskRoomToTimeParamVO.getTimeId()))
                .collect(Collectors.toList());
        //3、遍历步骤1中当前批次绑定的场次信息，找出步骤2中是否存在考场id相同 且 批次时间与当前批次时间重叠的场次
        //构造当前批次的时间段
        Timeslot timeslot = TimeslotUtil.buildSlot(time.getStartTime(), time.getEndTime());
        //创建用于存放批次时间重叠的Map，TimeRoomInfoDTO是当前批次下的场次信息，List<TimeRoomInfoDTO>是与当前批次绑定了相同考场 且批次时间与当前批次时间重叠的场次信息
        Map<TimeRoomInfoDTO, List<TimeRoomInfoDTO>> overlappedMap = new HashMap<>();
        timeRoomInfoDTOList.forEach(timeRoomInfoDTO -> {
            //筛选出考场id与当前场次相同、且批次时间段与当前场次重叠的场次
            List<TimeRoomInfoDTO> list = othersList.stream().filter(other -> {
                if (other.getRoomId().equals(timeRoomInfoDTO.getRoomId())) {
                    //构造当前遍历批次时间段
                    Timeslot currentTimeslot = TimeslotUtil.buildSlot(other.getStartTime(), other.getEndTime());
                    //判断当前批次和当前遍历批次的时间段是否重叠
                    return TimeslotUtil.overlapped(timeslot, currentTimeslot);
                }
                return false;
            }).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(list)) {
                overlappedMap.put(timeRoomInfoDTO, list);
            }
        });

        if (!CollectionUtils.isEmpty(overlappedMap)) {
            //当前批次中绑定的考场“考场X”已被批次“批次A、批次B、批次C”绑定，且当前批次与批次“批次A、批次B、批次C”的时间冲突
            List<String> messageList = overlappedMap.entrySet().stream().map(entry -> {
                //获取时间重叠的批次名称
                String timeNames = entry.getValue().stream()
                        .map(TimeRoomInfoDTO::getTimeName)
                        .collect(Collectors.joining("、"));

                return "当前批次中绑定的考场" + "【" + entry.getKey().getRoomName()
                        + "】" + "已被批次" + "【" + timeNames + "】" + "绑定，且当前批次与批次"
                        + "【" + timeNames + "】" + "的时间冲突;";
            }).collect(Collectors.toList());
            throw new KqException(ResponseCode.TIMES_OVERLAP.getCode(),
                    ResponseCode.TIMES_OVERLAP.getMsg(), messageList);
        }
    }

    /**
     * 获取当前最大场次编号
     * @param taskId 考试任务id
     * @return 当前最大场次编号
     */
    private int getLastTimeRoomCode(String taskId) {
        QueryWrapper<TimeRoom> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .select(TimeRoom::getCode)
                .eq(TimeRoom::getTaskId, taskId)
                .eq(TimeRoom::getDelFlag, false)
                .last("ORDER BY CAST(code AS SIGNED) DESC LIMIT 1");
        TimeRoom timeRoom = getOne(queryWrapper);
        String code = Optional.ofNullable(timeRoom)
                .map(TimeRoom::getCode)
                .orElse("0");
        return Integer.parseInt(code);
    }

    @Override
    public List<RoomDataPackageDTO> getRoomDataPackageDTOList(String taskCode, String roomCode) {
        return baseMapper.getRoomDataPackageDTOList(taskCode, roomCode);
    }

    @Override
    public List<RoomDataPackageDTO> getMockRoomDataPackageDTOList(String taskCode, String roomCode) {
        return baseMapper.getMockRoomDataPackageDTOList(taskCode, roomCode);
    }

    @Override
    public List<TimeRoomInfoResultDTO> getTimeRoomInfoResultDTO(String taskCode, String placeCode) {
        return baseMapper.getTimeRoomInfoResultDTO(taskCode, placeCode);
    }

    @Override
    public TimeRoom getByTaskTimeRoomCode(String taskCode, String timeRoomCode) {
        return baseMapper.getByTaskTimeRoomCode(taskCode, timeRoomCode);
    }

    @Override
    public List<TimeRoom> getByTaskIdAndTimeId(String taskId, String timeId) {
        return baseMapper.getByTaskIdAndTimeId(taskId, timeId);
//        return lambdaQuery().eq(TimeRoom::getTaskId,taskId)
//                .eq(TimeRoom::getTimeId,timeId)
//                .eq(TimeRoom::getDelFlag, false)
//                .orderByAsc(TimeRoom::getCode)
//                .list();
    }
    @Override
    public List<TimeRoom> getByTaskId(String taskId) {
        QueryWrapper<TimeRoom> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(TimeRoom::getTaskId, taskId)
                .eq(TimeRoom::getDelFlag, false);
        return list(queryWrapper);
    }

    @Override
    public void deleteRoomDataByTaskId(String taskId) {
        this.lambdaUpdate()
                .eq(TimeRoom::getTaskId, taskId)
                .eq(TimeRoom::getDelFlag, false)
                .set(TimeRoom::getRoomDataFileId, null)
                .update();
    }

    @Override
    public List<ObtRoomData> getObtRoomDataList(String taskId) {
        return baseMapper.getRooDataList(taskId);
    }

    @Override
    public List<GceRoomData> getGceRoomDataList(String taskId) {
        return baseMapper.getGceRoomDataList(taskId);
    }

    @Override
    public Page<RoomPackageVO> getPageRoomPackage(PageRoomPackageParam param) {
        return baseMapper.getPageRoomPackage(param);
    }

    @Override
    public List<TaskPlaceVO> selectNoArrangePlaceIds(String taskId) {

        return baseMapper.selectNoArrangePlaceIds(taskId);
    }
    @Override
    public List<TimeRoomVO> selectTimeRoomByPlaceId(String taskId, String placeId) {
        return baseMapper.selectTimeRoomByPlaceId(taskId, placeId);
    }

    @Override
    public List<UnArrangeRoomTimeInfoVO> selectUnArrangeTimeRoomInfo(String taskId) {
        return baseMapper.selectUnArrangeTimeRoomInfo(taskId);
    }

    @Override
    public void deleteAllByTaskId(String taskId) {
        QueryWrapper<TimeRoom> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(TimeRoom::getTaskId, taskId);
        remove(queryWrapper);
    }

    @Override
    public List<RoomExaminerDTO> getRoomExaminerDTO(String taskId, String timeId) {
        return baseMapper.getRoomExaminerDTO(taskId, timeId);
    }

    @Override
    public Map<String,List<TimeRoom>> getTaskTimeRoomAndCreate(Map<String,List<CandidateSubjectDTO>> timeCandidateMap,
                                                               String taskId, Integer roomMaxCandidateCount) {

        Map<String,List<TimeRoom>> timmRoomMap = new HashMap<>();

        final int[] maxTimeCode = {getLastTimeRoomCode(taskId)};

        timeCandidateMap.forEach((timeId,timeCandidates) -> {

            if (timeCandidates == null || timeCandidates.isEmpty()) {
                return;
            }

            //组装场次数据
            List<TimeRoom> timeRooms = getByTaskIdAndTimeId(taskId,timeId);
            if (timeRooms == null || timeRooms.isEmpty()) {

                //当前批次的考生数
                Integer timeCandidateCountMax = timeCandidates.size();

                //当前批次需要的考场数。
                int roomCounts = (int) Math.ceil(timeCandidateCountMax.doubleValue() / roomMaxCandidateCount.doubleValue());

                //获取所有的考场和创建
                List<TaskRoom> taskRooms = iTaskRoomService.getTaskRoomAndCreate(taskId, roomCounts, roomMaxCandidateCount);

                //全量
                for (int i = 0; i < roomCounts; i++) {

                    maxTimeCode[0] = maxTimeCode[0] + 1;
                    TaskRoom taskRoom = taskRooms.get(i);
                    TimeRoom timeRoom = new TimeRoom();
                    timeRoom.setTimeId(timeId)
                            .setTaskId(taskId)
                            .setRoomId(taskRoom.getId())
                            .setCode(String.valueOf(maxTimeCode[0]))
                            .setMaxCount(taskRoom.getSeatNum())
                            .setPlanCount(taskRoom.getSeatNum())
                            .setActualCount(0)
                            .setId(UUIDUtils.newSortUUID());
                    timeRooms.add(timeRoom);
                }

                //保存入库
                this.saveBatch(timeRooms);

            } else {

                int[] planCount = {0};
                timeRooms.stream().forEach(timeRoom -> {
                    planCount[0] = planCount[0] + timeRoom.getPlanCount() - timeRoom.getActualCount();
                });

                //当前批次的考生数
                Integer timeCandidateCountMax = timeCandidates.size() - planCount[0];

                //当前考生大于计划编排数，才允许新增考场
                if (timeCandidateCountMax > 0) {

                    //当前批次需要的考场数。
                    int roomCounts = (int) Math.ceil(timeCandidateCountMax.doubleValue() / roomMaxCandidateCount.doubleValue());
                    roomCounts = roomCounts + timeRooms.size();

                    //获取所有的考场和创建
                    List<TaskRoom> taskRooms = iTaskRoomService.getTaskRoomAndCreate(taskId, roomCounts, roomMaxCandidateCount);

                    //增量，获取数据库中数据
                    int currentCount = timeRooms.size();

                    List<TimeRoom> newTimeRoomList = new ArrayList<>();
                    for (int i = currentCount; i < roomCounts; i++) {
                        TaskRoom taskRoom = taskRooms.get(i);
                        TimeRoom timeRoom = new TimeRoom();
                        maxTimeCode[0] = maxTimeCode[0] + 1;
                        timeRoom.setTimeId(timeId)
                                .setTaskId(taskId)
                                .setRoomId(taskRoom.getId())
                                .setCode(String.valueOf(maxTimeCode[0]))
                                .setMaxCount(taskRoom.getSeatNum())
                                .setPlanCount(taskRoom.getSeatNum())
                                .setActualCount(0)
                                .setId(UUIDUtils.newSortUUID());
                        newTimeRoomList.add(timeRoom);

                    }
                    //保存入库
                    if (newTimeRoomList != null && !newTimeRoomList.isEmpty()) {
                        this.saveBatch(newTimeRoomList);
                        timeRooms.addAll(newTimeRoomList);
                    }
                }
            }

            timmRoomMap.put(timeId,timeRooms);
        });

        return timmRoomMap;
    }

    @Override
    public boolean whetherEditTimeRoom(String taskId) {
        //获取任务考试类型
        Task task = iTaskService.getTaskById(taskId);
        String examType = Optional.ofNullable(task)
                .map(Task::getExamType)
                .orElseThrow(() -> new KqException(ResponseCode.TASK_NOT_FOUND.getCode(),ResponseCode.TASK_NOT_FOUND.getMsg()));
        if (TaskExamTypeEnum.OFFLINE.getCode().equals(examType)) {
            //线下机考，允许修改场次信息
            return true;
        }

        //线上模式，虚拟考场，不允许修改
        return !iTimeRoomParamService.AreVirtualRoom(taskId);
    }
    @Override
    public Map<String,List<TimeRoom>> getTaskTimeRoomMap(String taskId) {
        List<TimeRoom> timeRoomList = getByTaskId(taskId);

        if (timeRoomList == null || timeRoomList.isEmpty()) {
            return null;
        }

        Map<String,List<TimeRoom>> map = new HashMap<>();
        map = timeRoomList.stream().collect(Collectors.groupingBy(TimeRoom::getTimeId));
        return map;
    }

    @Override
    public Map<String,Integer> getTimeRoomMap(String taskId) {
        List<TimeRoom> timeRoomList = getByTaskId(taskId);

        if (timeRoomList == null || timeRoomList.isEmpty()) {
            return null;
        }
        Map<String,Integer> map = new HashMap<>();
        timeRoomList.stream().forEach(timeRoom -> {
            map.put(timeRoom.getTimeId()+"#"+timeRoom.getRoomId(),timeRoom.getMaxCount()-timeRoom.getActualCount());
        });
        return map;
    }

    @Override
    public List<TimeRoom> listByTaskRoomId(String taskId, String roomId) {
        return this.lambdaQuery()
                .eq(TimeRoom::getTaskId, taskId)
                .eq(TimeRoom::getRoomId, roomId)
                .eq(TimeRoom::getDelFlag, false)
                .list();
    }

    @Override
    public Optional<TimeRoom> getByTaskTimeRoomCode(String taskCode, String timeCode, String roomCode) {
        return Optional.ofNullable(baseMapper.getByTimeRoomCode(taskCode, timeCode, roomCode));
    }

    @Override
    public boolean hasTimeRoom(String taskId, String timeId, String roomId) {
        return this.lambdaQuery().select(TimeRoom::getId)
                .eq(TimeRoom::getTaskId, taskId)
                .eq(TimeRoom::getTimeId, timeId)
                .eq(TimeRoom::getRoomId, roomId)
                .eq(TimeRoom::getDelFlag, false)
                .last("LIMIT 1")
                .oneOpt()
                .isPresent();
    }

    @Override
    public TimeRoom one(String taskId, String timeId, String roomId) {
        return this.lambdaQuery()
                .eq(TimeRoom::getTaskId, taskId)
                .eq(TimeRoom::getTimeId, timeId)
                .eq(TimeRoom::getRoomId, roomId)
                .eq(TimeRoom::getDelFlag, false)
                .last("LIMIT 1")
                .one();
    }

    @Override
    public Boolean AreTimeRoomData(String taskId) {
        Integer count = this.lambdaQuery()
                .eq(TimeRoom::getTaskId, taskId)
                .eq(TimeRoom::getDelFlag, false)
                .count();
        if (count > 0) {
            return true;
        }
        return false;
    }

    @Override
    public List<TimeRoomCodeVO> selectTimeRoomCodes(String examId) {
        return baseMapper.selectTimeRoomCodes(examId);
    }

    /**
     * 验证是否编排
     * @param param
     */
    @Override
    public void checkArrangeCandidate(TaskRoomToTimeParamVO param){
        Task task = iTaskService.getTaskById(param.getTaskId());
        //TODO: 校验任务数据锁定状态(后续版本考虑优化为从缓存中获取状态)
        iTaskService.isLockDataThrowException(task);

        //线上模式虚拟考场，不允许编辑
        if (!this.whetherEditTimeRoom(param.getTaskId())) {
            throw new KqException(ResponseCode.TIMES_ONLINE_TIME_ROOM_NOT_EDIT.getCode(),
                    ResponseCode.TIMES_ONLINE_TIME_ROOM_NOT_EDIT.getMsg());
        }
//        if (TaskExamTypeEnum.ONLINE.getCode().equals(task.getExamType())) {
//            throw new KwGovException(ResponseCode.TIMES_ONLINE_TIME_ROOM_NOT_EDIT,"线上模式，不允许编辑批次考场数据。");
//        }

        List<TaskTimeRoomArrangeOrNoVO> voList = param.getTaskTimeRoomArrangeOrNoVOList();
        //要删除批次考场关系ids
        List<String> timeRoomIds = null;
        if(TextUtil.isNotNull(voList)){
            timeRoomIds =  voList.stream().map(TaskTimeRoomArrangeOrNoVO::getTimeRoomId).collect(Collectors.toList());
        }

        //1.获取当前批次编各考场排考生数统计
        List<TimeRoomCandidateReport> timeCandidateReports = timeMapper.queryTimeCandidateReport(param.getTimeId() ,timeRoomIds);
        //2.如果打捞数据存在
        if(TextUtil.isNotNull(timeCandidateReports)){
            List<String> roomNames = new ArrayList<>();
            final int zero = 0;
            //遍历入参
            timeCandidateReports.forEach(item->{
                //若统计不为空并且有已编排考生
                if(item.getCandidateCount() > zero){
                    roomNames.add(item.getRoomName());
                }
            });
            //如果名称列表不为空拼接并抛出错误提示
            if(!roomNames.isEmpty()){
                throw new KqException(ResponseCode.COMMON_PARAM_INVALID.getCode(),String.join(",", roomNames) +"存在已编排考生，无法执行该操作");
            }

        }
    }

}
